#include "Diamond.h"
#include "GameUtils.h"
#include "Vector.h"
#include "Utils.h"
#include "Animation.h"
#include "AnimationSprite.h"
#include "ResMgr.h"
#include "Board.h"
#include "Cell.h"
#include "math.h"

Diamond::Diamond():MoveableObj()
{
	classID = CID_DIAMOND;
	cell = 0;
	isTouchDown = false;
	angle = angleForAnimation = 0.0f;
	oldPos = new Vector();
	length = 0;
	oldState = DMS_IDLE;
	isAddedToAteList = false;
	isActive = false;
	isExploding = false;
	dxVibrate = pos->x;
	isVibrate = false;
	amplitude = 0;
	isEnableVibrate = true;
}


Diamond::Diamond(int id, Board* board):MoveableObj()
{
	classID = CID_DIAMOND;
	cell = 0;
	this->board = board;
	

	Animation* ani = new Animation(DM_ANI_DIAMOND);
	ani->GenerateAuto(ResMgr::GetInstance()->GetTexture(TID_FRUIT),
		32, 32, 4, 0, 9, 0);
	aniSprite->AddAnimation(ani);

	ani = new Animation(DM_ANI_EXPLODING);
	ani->GenerateAuto(ResMgr::GetInstance()->GetTexture(TID_EXPLODING),
		32, 32, 5, 0, 19, 0.025);
	aniSprite->AddAnimation(ani);

	ani = new Animation(DM_ANI_EFFECT_EAT4);
	ani->GenerateAuto(ResMgr::GetInstance()->GetTexture(TID_EFFECT_SPECIAL_DIA),
		40, 40, 5, 0, 9, 0.05);
	aniSprite->AddAnimation(ani);

	ani = new Animation(DM_ANI_EFFECT_EAT5);
	ani->GenerateAuto(ResMgr::GetInstance()->GetTexture(TID_EFFECT_SPECIAL_DIA),
		40, 40, 5, 10, 19, 0.05);
	aniSprite->AddAnimation(ani);

	ani = new Animation(DM_ANI_EFFECT_EAT5X5);
	// TODO : Replace this animation with another effect.
	ani->GenerateAuto(ResMgr::GetInstance()->GetTexture(TID_EFFECT_SPECIAL_DIA),
		40, 40, 5, 10, 19, 0.05);
	aniSprite->AddAnimation(ani);

	SetID(id);
	isTouchDown = false;
	angle = angleForAnimation = 0.0f;
	oldPos = new Vector();
	nextPos = new Vector();
	length = 0;
	oldState = DMS_IDLE;
	isAddedToAteList = false;
	isActive = false;
	isExploding = false;
	dxVibrate = pos->x;
	isVibrate = false;
	amplitude = 0;
	isEnableVibrate = true;
}

void Diamond::Update()
{
	if(v->y>V_LIMIT)
		v->y = V_LIMIT;

	if(isActive == true)
	{
		angleForAnimation+=0.3;
		angle = 30*sin(angleForAnimation);
	}

	if(isExploding == true)
	{
		if(aniSprite->Update(DM_ANI_EXPLODING)==true)
			SetState(DMS_DIE);
	}
	if(effect != DME_NORMAL)
	{
		switch(effect)
		{
		case DME_EAT4:
			aniSprite->Update(DM_ANI_EFFECT_EAT4);
			break;
		case DME_EAT5:
			aniSprite->Update(DM_ANI_EFFECT_EAT5);
			break;
		}
	}

	if(isVibrate == true && isEnableVibrate == true)
	{
		angleToCalculateDxVibrate += ANGLE_FOR_AMPLITUDE_INCREASE;
		dxVibrate = amplitude*sin(angleToCalculateDxVibrate);
		amplitude -= REDUCE_AMPLITUDE;
		if(amplitude < 0)
		{
			amplitude = 0;
			isVibrate = false;
		}
	}

	switch(state)
	{
		
	case DMS_DROPDOWN_AUTO:
		UpdatePosition();
		CheckStopWhenDrop();
		break;
	case DMS_MOVE:
		UpdatePosition();
		switch(directionMove)
		{
		case DMD_LEFT:
			length = oldPos->x - pos->x;
			break;
		case DMD_RIGHT:
			length = pos->x - oldPos->x;
			break;
		case DMD_TOP:
			length = oldPos->y - pos->y;
			break;
		case DMD_BOTTOM:
			length = pos->y - oldPos->y;
			break;
		}
		if(length>board->GetCellWidth())
		{
			pos->Set(nextPos->x, nextPos->y);
			if(isReturn == false)
			{
				SetState(DMS_IDLE);
			}
			else Move(GetRevertDir(directionMove), false);
		}
		break;
	}
}

int Diamond::GetRevertDir(int direction)
{
	switch(direction)
	{
	case DMD_LEFT:
		return DMD_RIGHT;
	case DMD_RIGHT:
		return DMD_LEFT;
	case DMD_TOP:
		return DMD_BOTTOM;
	case DMD_BOTTOM:
		return DMD_TOP;
	}
}

void Diamond::CheckStopWhenDrop()
{
	int xBoard, yBoard;
	xBoard = (pos->x - board->pos->x )/board->GetCellWidth();
	yBoard = (pos->y - board->pos->y )/board->GetCellHeight();
	Cell* nextCell = board->GetCell(xBoard, yBoard+1);
	Cell* curCell = board->GetCell(xBoard, yBoard);

	bool isContinue = false;
	int yBoardTemp = yBoard;
	while (nextCell != 0)
	{
		if(nextCell->GetIsAvailable() == true && nextCell->GetDiamond() == 0)
		{
			isContinue = true;
			break;
		}
		yBoardTemp++;
		nextCell = board->GetCell(xBoard, yBoardTemp+1);
	}
	
	nextCell = board->GetCell(xBoard, yBoard+1);
	if(isContinue == true)
	{
		if(nextCell!=0)		//next cell is exist
		{
			Diamond* diamond = board->GetCell(xBoard, yBoard+1)->GetDiamond();
			if(diamond != 0)
			{
				if(diamond->GetState()==DMS_IDLE)
				{
						StopDiamondWhenNextCellNotEmpty(xBoard, yBoard);
				}
			}
		}
	}
	else 
		StopDiamondWhenNextCellNotEmpty(xBoard, yBoard);
}

void Diamond::StopDiamondWhenNextCellNotEmpty(int xBoard, int yBoard)
{
	// while cell had diamond and yBoard valid and
	// diamond in current cell different itself, loop to decrease yBoard
	Cell* curCell = board->GetCell(xBoard, yBoard);
	if(curCell == 0) return;
	Diamond* curDiamondInCell = curCell->GetDiamond();
	
	/*while(curCell->GetIsAvailable() == false)
	{
		yBoard--;
		curCell = board->GetCell(xBoard, yBoard);
	}*/

	while((curDiamondInCell!=0 && yBoard>0 && curDiamondInCell!=this) || curCell->GetIsAvailable() == false)
	{
		yBoard--;
		curCell = board->GetCell(xBoard, yBoard);
		curDiamondInCell = curCell->GetDiamond();
	}

	SetState(DMS_IDLE);
	//vibrate
	Cell* vibCell = 0;
	Diamond* vibDiamond = 0;
	
	vibCell = board->GetCell(xBoard, yBoard + 1);
	if(vibCell != 0) vibDiamond = vibCell->GetDiamond();
	if(vibDiamond != 0) if(vibDiamond->GetIsVibrate()==false)
		vibDiamond->Vibrate(VIBRATE_AMTITUDE_1);
	
	vibCell = board->GetCell(xBoard, yBoard + 2);
	if(vibCell != 0) vibDiamond = vibCell->GetDiamond();
	if(vibDiamond != 0) if(vibDiamond->GetIsVibrate()==false)
		vibDiamond->Vibrate(VIBRATE_AMTITUDE_2);

	vibCell = board->GetCell(xBoard, yBoard + 3);
	if(vibCell != 0) vibDiamond = vibCell->GetDiamond();
	if(vibDiamond != 0) if(vibDiamond->GetIsVibrate()==false) 
		vibDiamond->Vibrate(VIBRATE_AMTITUDE_3);
	
	if(cell != 0) cell->SetDiamond(0);
	pos->y = board->pos->y + yBoard*board->GetCellHeight();
	board->GetCell(xBoard, yBoard)->SetDiamond(this);
}

void Diamond::ResetPositionToItsCell()
{
	SetState(DMS_IDLE);
	if(cell != 0)
	{
		pos->x = board->pos->x + cell->GetPosInBoard()->x * board->GetCellWidth();
		pos->y = board->pos->y + cell->GetPosInBoard()->y * board->GetCellHeight();
	}
}

void Diamond::Move(int direction, bool isReturn)
{
	switch(direction)
	{
	case DMD_LEFT:
		v->x = 0;
		a->x = -A_MOVE;
		nextPos->Set(pos->x-board->GetCellWidth(), pos->y);
		break;
	case DMD_RIGHT:
		v->x = 0;
		a->x = A_MOVE;
		nextPos->Set(pos->x + board->GetCellWidth(), pos->y);
		break;
	case DMD_TOP:
		v->y = 0;
		a->y = -A_MOVE;
		nextPos->Set(pos->x, pos->y - board->GetCellHeight());
		break;
	case DMD_BOTTOM:
		v->y = 0;
		a->y = A_MOVE;
		nextPos->Set(pos->x, pos->y + board->GetCellHeight());
		break;
	}
	this->isReturn = isReturn;
	directionMove = direction;
	oldPos->Set(pos->x, pos->y);
	SetState(DMS_MOVE);
}

void Diamond::Render()
{
	float x, y;
	if(isVibrate == false)
	{
		x = pos->x; y = pos->y;
	}
	else{
		x = pos->x + dxVibrate;
		y = pos->y;
	}

	if(state!=DMS_DIE)
	{
		if(isExploding == true)
			aniSprite->SetCurAnimation(DM_ANI_EXPLODING);
		else aniSprite->SetCurAnimation(DM_ANI_DIAMOND);

		aniSprite->Render(x, y, 1.25, 1.25, angle, 1, 0.6);
	}

	switch(effect)
	{
		case DME_EAT4:
			aniSprite->SetCurAnimation(DM_ANI_EFFECT_EAT4);
			aniSprite->Render(x - (SCALE_EFFECT -1.0f)*W_SPRITE_EFFECT/2,
				y-(SCALE_EFFECT -1.0f)*H_SPRITE_EFFECT/2, 1.250f, 1.250f, 0, 1, 0.59);
			break;
		case DME_EAT5:
			aniSprite->SetCurAnimation(DM_ANI_EFFECT_EAT5);
			aniSprite->Render(x - (SCALE_EFFECT -1.0f)*W_SPRITE_EFFECT/2,
				y-(SCALE_EFFECT -1.0f)*H_SPRITE_EFFECT/2, 1.250f, 1.250f, 0, 1, 0.59);
			break;
		case DME_EAT5X5:
			aniSprite->SetCurAnimation(DM_ANI_EFFECT_EAT5X5);
			aniSprite->Render(x - (SCALE_EFFECT -1.0f)*W_SPRITE_EFFECT/2,
				y-(SCALE_EFFECT -1.0f)*H_SPRITE_EFFECT/2, 1.250f, 1.250f, 0, 1, 0.59);
			break;
	}
}

void Diamond::SetState(int state)
{
	oldState = this->state;
	this->state = state;
	switch(state)
	{
	case DMS_IDLE:
		a->x = a->y = v->x = v->y = 0;
		SetIsActive(false);
		break;
	case DMS_DROPDOWN_AUTO:
		a->y = A_DROP;
		break;
	}
}

void Diamond::SetIsExploding(bool isExploding)
{
	this->isExploding = isExploding;
}
Diamond::~Diamond()
{
	DELETE_SAFELY(nextPos);
	DELETE_SAFELY(oldPos);
}

void Diamond::SetID(int id)
{
	this->id = id;
	aniSprite->GetAnimation(0)->SetSpriteIndex(true, id);
}

void Diamond::SetCell(Cell* cell)
{
	this->cell = cell;
}

void Diamond::SetIsActive(bool isActive)
{
	this->isActive = isActive;
	angle = 0;
}

void Diamond::Vibrate(float amplitude)
{
	this->isVibrate = true;
	dxVibrate = 0;
	this->amplitude = amplitude;
	angleToCalculateDxVibrate = 0;
}
void Diamond::SetIsEnableVibrate(bool isEnableVibrate)
{
	this->isEnableVibrate = isEnableVibrate;
	isVibrate = false;
}

void Diamond::SetEffect(int effect)
{
	this->effect = effect;
}